
<!DOCTYPE html>
<html>
  <head>
    
<meta charset="utf-8" >

<title>c++使用7年后的经验总结 | dragon</title>
<meta name="description" content="邮箱(base64)：MTY5MDMwMjk2M0BxcS5jb20=
">

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.min.css">

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<link rel="shortcut icon" href="https://dragonfive.gitee.io//favicon.ico?v=1740893463017">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0/katex.min.css">
<link rel="stylesheet" href="https://dragonfive.gitee.io//styles/main.css">



<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.5.1/build/highlight.min.js"></script>



  </head>
  <body>
    <div id="app" class="main">
      <div class="site-header-container">
  <div class="site-header">
    <div class="left">
      <a href="https://dragonfive.gitee.io/">
        <img class="avatar" src="https://dragonfive.gitee.io//images/avatar.png?v=1740893463017" alt="" width="32px" height="32px">
      </a>
      <a href="https://dragonfive.gitee.io/">
        <h1 class="site-title">dragon</h1>
      </a>
    </div>
    <div class="right">
      <transition name="fade">
        <i class="icon" :class="{ 'icon-close-outline': menuVisible, 'icon-menu-outline': !menuVisible }" @click="menuVisible = !menuVisible"></i>
      </transition>
    </div>
  </div>
</div>

<transition name="fade">
  <div class="menu-container" style="display: none;" v-show="menuVisible">
    <div class="menu-list">
      
        
          <a href="/" class="menu purple-link">
            首页
          </a>
        
      
        
          <a href="/archives" class="menu purple-link">
            归档
          </a>
        
      
        
          <a href="/tags" class="menu purple-link">
            标签
          </a>
        
      
        
          <a href="/post/about" class="menu purple-link">
            关于
          </a>
        
      
    </div>
  </div>
</transition>


      <div class="content-container">
        <div class="post-detail">
          
          <h2 class="post-title">c++使用7年后的经验总结</h2>
          <div class="post-info post-detail-info">
            <span><i class="icon-calendar-outline"></i> 2017-07-31</span>
            
              <span>
                <i class="icon-pricetags-outline"></i>
                
                  <a href="https://dragonfive.gitee.io/tag/Tl2aOZ6vP/">
                    cpp
                    
                  </a>
                
              </span>
            
          </div>
          <div class="post-content" v-pre>
            <hr>
<p>title: c++使用7年后的经验总结<br>
date: 2017/7/31 17:38:58</p>
<p>categories:</p>
<ul>
<li>编程<br>
tags:</li>
<li>cpp</li>
</ul>
<hr>
<p>[TOC]</p>
<p>一年前写了一篇<a href="https://zhuanlan.zhihu.com/p/21930436">c++11新特性详解</a>, 这里总结了一些c++的编程经验。</p>
<figure data-type="image" tabindex="1"><img src="https://www.github.com/DragonFive/CVBasicOp/raw/master/1505722824118.jpg" alt="enter description here" loading="lazy"></figure>
<!--more-->
<h1 id="c11-new-feature">c++11 new feature</h1>
<h2 id="variadic-templates">variadic Templates</h2>
<p><strong>可变的模板参数</strong>，参数量可以变化，举个例子：</p>
<h3 id="对于函数">对于函数</h3>
<pre><code class="language-cpp">void print()//对于print没有参数的时候，调用这个函数。
{

}

template &lt;typename T, typename... Types&gt;
void print(const T&amp; firstArg, const Types&amp;... args)
{
	cout&lt;&lt;firstArg&lt;&lt;endl;
	print(args...);//这里使用了递归的方式
}

//下面的调用方式
print(7.5, &quot;hello&quot;, 1.3, bitset&lt;16&gt;(377));

</code></pre>
<p>注意了，上面的这种方式由于不知道有多少个参数，所以使用了递归的方式。为了<strong>处理最后的状态</strong>，上面声明了零参数的print;</p>
<p>如果想知道args有多少个，可以使用sizeof...(args);</p>
<p>这种方式可以把参数逐一分开</p>
<h3 id="对于类">对于类</h3>
<p>tuple的实现就是依赖可变参数</p>
<pre><code class="language-cpp">template &lt;typename... Values&gt; class tuple;
template&lt;&gt; class tuple&lt;&gt;(); // 处理终止条件

template&lt;typename Head, typename... Tail&gt;
class tuple&lt;Head, Tail...&gt; :private tuple&lt;Tail...&gt;//实现递归定义
{
		typedef tuple&lt;Tail...&gt; inherited;// 重新起个名字;
	public :
		tuple(){}
		tuple(Head v, Tail... vtail):m_head(v), inherited(vtail){}
	protected:
		Head m_head;
};

</code></pre>
<h2 id="右值引用移动构造函数与-move函数">右值引用，移动构造函数，与 move函数</h2>
<p><a href="https://mp.weixin.qq.com/s?__biz=MzIwNTc4NTEwOQ==&amp;mid=2247483724&amp;idx=1&amp;sn=e6b4304547607ba7d7714716913f5259&amp;chksm=972ad036a05d592076dc6f37a3df5cc5ee18678eae56c35d5008c7ff046ec5239b5bf55eb9c0&amp;mpshare=1&amp;scene=1&amp;srcid=0825UmZbQZ3YCp4bvw8rWvlT&amp;pass_ticket=gVdGvS19sLXA%2FE1%2BMc0kM3FFWwFheq%2FmuFwE1zEFYm9r5SAU364mtiB7sMLj0ez3#rd">C++札记 C++11中右值引用与移动构造函数</a></p>
<p><strong>左值与右值</strong></p>
<p>C++中，可以这么理解：对于一个表达式，凡是对其取地址（&amp;）操作可以成功的都是左值，否则就是右值。</p>
<p>需要注意的是：临时对象是右值 。</p>
<p><strong>左值引用与右值引用</strong></p>
<p>右值引用的形式为：类型 &amp;&amp; a= 被引用的对象 ，此外需要注意的是<strong>右值引用只能绑定到右值</strong>，如int &amp;&amp; x = 3;而形如 int x = 3; int &amp;&amp; y = x则是错误的，因为x是一个左值。</p>
<p>左值引用只能绑定到左值上。</p>
<p><strong>拷贝构造函数与移动构造函数</strong></p>
<p>拷贝构造函数就是需要拷贝一份样本给自己对象，而移动构造函数就是把样本的内存拿过来自己用。</p>
<pre><code class="language-cpp">Test(Test &amp;&amp; rhs):m_p(rhs.m_p)
{
    rhs.m_p = nullptr;
}

</code></pre>
<p>但是移动构造函数只能接受右值，所以如果是左值想传入，需要使用std::move()函数。</p>
<h2 id="智能指针">智能指针</h2>
<p><a href="https://mp.weixin.qq.com/s?__biz=MzIwNTc4NTEwOQ==&amp;mid=2247483809&amp;idx=1&amp;sn=373d64600b944be7258304119dae247e&amp;chksm=972ad0dba05d59cd6cf08f3874db2c72f6a66f66ab8f516cc576c3e8752c3aa6c385e936696e&amp;mpshare=1&amp;scene=21&amp;srcid=1007uaWOzcd3a12MYWEEsptn#wechat_redirect">C++智能指针</a></p>
<p>C++ STL为我们提供了四种智能指针：<strong>auto_ptr、unique_ptr、shared_ptr和weak_ptr</strong>；其中auto_ptr是C++98提供，在C++11中建议摒弃不用。</p>
<h3 id="为什么不建议使用auto_ptr">为什么不建议使用auto_ptr</h3>
<pre><code class="language-cpp">auto_ptr&lt;int&gt; px(new int(8));
auto_ptr&lt;int&gt; py;
py = px;
</code></pre>
<p>为了避免上述的代码把内存delete两次，智能指针有两种机制。</p>
<ol>
<li>建立所有权，一个对象同一时刻智能有一个智能指针可以拥有。只有拥有所有权的智能智能才能delete这个对象，unique_ptr和auto_ptr就是这种机制</li>
<li>引用计数，share_ptr就是这么做的</li>
</ol>
<p>当py=px，后如果再想访问px, 对于auto_ptr会在运行时报segment_fault的错误，而对于unique_ptr则会在编译期就报错。</p>
<h3 id="自己实现share_pre">自己实现share_pre</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

template&lt;class T&gt;
class SmartPtr
{
public:
    SmartPtr(T *p);
    ~SmartPtr();
    SmartPtr(const SmartPtr&lt;T&gt; &amp;orig);                // 浅拷贝
    SmartPtr&lt;T&gt;&amp; operator=(const SmartPtr&lt;T&gt; &amp;rhs);    // 浅拷贝
private:
    T *ptr;
    // 将use_count声明成指针是为了方便对其的递增或递减操作
    int *use_count;
};

template&lt;class T&gt;
SmartPtr&lt;T&gt;::SmartPtr(T *p) : ptr(p)
{
    try
    {
        use_count = new int(1);
    }
    catch (...)
    {
        delete ptr;
        ptr = nullptr;
        use_count = nullptr;
        cout &lt;&lt; &quot;Allocate memory for use_count fails.&quot; &lt;&lt; endl;
        exit(1);
    }

    cout &lt;&lt; &quot;Constructor is called!&quot; &lt;&lt; endl;
}

template&lt;class T&gt;
SmartPtr&lt;T&gt;::~SmartPtr()
{
    // 只在最后一个对象引用ptr时才释放内存
    if (--(*use_count) == 0)
    {
        delete ptr;
        delete use_count;
        ptr = nullptr;
        use_count = nullptr;
        cout &lt;&lt; &quot;Destructor is called!&quot; &lt;&lt; endl;
    }
}

template&lt;class T&gt;
SmartPtr&lt;T&gt;::SmartPtr(const SmartPtr&lt;T&gt; &amp;orig)
{
    ptr = orig.ptr;
    use_count = orig.use_count;
    ++(*use_count);
    cout &lt;&lt; &quot;Copy constructor is called!&quot; &lt;&lt; endl;
}

// 重载等号函数不同于复制构造函数，即等号左边的对象可能已经指向某块内存。
// 这样，我们就得先判断左边对象指向的内存已经被引用的次数。如果次数为1，
// 表明我们可以释放这块内存；反之则不释放，由其他对象来释放。
template&lt;class T&gt;
SmartPtr&lt;T&gt;&amp; SmartPtr&lt;T&gt;::operator=(const SmartPtr&lt;T&gt; &amp;rhs)
{
    // 《C++ primer》：“这个赋值操作符在减少左操作数的使用计数之前使rhs的使用计数加1，
    // 从而防止自身赋值”而导致的提早释放内存
    ++(*rhs.use_count);

    // 将左操作数对象的使用计数减1，若该对象的使用计数减至0，则删除该对象
    if (--(*use_count) == 0)
    {
        delete ptr;
        delete use_count;
        cout &lt;&lt; &quot;Left side object is deleted!&quot; &lt;&lt; endl;
    }

    ptr = rhs.ptr;
    use_count = rhs.use_count;
    
    cout &lt;&lt; &quot;Assignment operator overloaded is called!&quot; &lt;&lt; endl;
    return *this;
}

</code></pre>
<h2 id="一些小更新">一些小更新</h2>
<h3 id="nullptr">nullptr</h3>
<p>可以用来替代NULL,避免因为NULL=0带来的一些混淆。</p>
<pre><code class="language-cpp">void f(int);
void f(void *);

//下面调用
f(0);//调用f(int)
f(NULL);//调用f(int)
f(nullptr);//调用f(void *)

</code></pre>
<h3 id="auto">auto</h3>
<p>当type比较长的时候，比如iterator定义的时候可以用auto替换，也可以定义返回值类型。</p>
<p>或者比较复杂：比如用</p>
<pre><code class="language-cpp">auto L = [](int x)-&gt;bool{};
</code></pre>
<h3 id="统一的初始化方法-大括号">统一的初始化方法-大括号</h3>
<pre><code class="language-cpp">int values[] {1,2,3};
vector&lt;int&gt; v{2,3,4,5};
</code></pre>
<h1 id="effective_cpp">effective_Cpp</h1>
<ol>
<li>constructors（构造函数）被声明为 explicit（显式）通常比 non-explicit（非显式）更可取，因为它们可以防止编译器执行意外的（常常是无意识的）type conversions（类型转换）。</li>
<li>copy constructor（拷贝构造函数）被用来以一个 object（对象）来初始化同类型的另一个 object（<strong>新对象</strong>），copy assignment operator（拷贝赋值运算符）被用来将一个 object（对象）中的值拷贝到同类型的另一个 object（对象）。</li>
<li>如果 const 出现在星号左边，则指针 pointed to（指向）的内容为 constant（常量）；如果 const 出现在星号右边，则 pointer itself（指针自身）为 constant。声明一个 iterator 为 const 就类似于声明一个 pointer（指针）为 const（也就是说，声明一个 T* const pointer（指针））：不能将这个 iterator 指向另外一件不同的东西，但是它所指向的东西本身可以变化。如果你要一个 iterator 指向一个不能变化的东西（也就是一个 const T* pointer（指针）的 STL 对等物），你需要一个 const_iterator：</li>
</ol>
<pre><code class="language-cpp">const std::vector&lt;int&gt;::iterator iter
std::vector&lt;int&gt;::const_iterator cIter

</code></pre>
<p>member functions（成员函数）在只有 constness（常量性）不同时是可以被 overloaded（重载）的，但这是 C++ 的一个重要特性。</p>
<pre><code class="language-cpp">  const char&amp; operator[](std::size_t position) const   // operator[] for
  { return text[position]; }                           // const objects

  char&amp; operator[](std::size_t position)               // operator[] for
  { return text[position]; }                           // non-const objects

</code></pre>
<p>改变一个返回 built-in type（内建类型）的函数的返回值总是非法的。</p>
<ol start="4">
<li>mutable 将 non-static data members（非静态数据成员）从 bitwise constness（二进制位常量性）的约束中解放出来：</li>
</ol>
<pre><code class="language-cpp">class CTextBlock {
public:


  std::size_t length() const;

private:
  char *pText;

  mutable std::size_t textLength;         // these data members may
  mutable bool lengthIsValid;             // always be modified, even in
};                                        // const member functions

std::size_t CTextBlock::length() const
{
  if (!lengthIsValid) {
    textLength = std::strlen(pText);      // now fine
    lengthIsValid = true;                 // also fine
  }

  return textLength;
}

</code></pre>
<ol start="5">
<li>根据 const member function（成员函数）实现它的 non-const 版本的技术却非常值得掌握</li>
</ol>
<pre><code class="language-cpp">class TextBlock {
public:

  ...

  const char&amp; operator[](std::size_t position) const     // same as before
  {
    ...
    ...
    ...
    return text[position];
  }

  char&amp; operator[](std::size_t position)         // now just calls const op[]
  {
    return
      const_cast&lt;char&amp;&gt;(                         // cast away const on op[]'s return type;
        static_cast&lt;const TextBlock&amp;&gt;(*this)     // add const to *this's type;
          [position]                             // call const version of op[]
      );
  }

...

};

</code></pre>
<ol start="6">
<li>通常它有更高的效率。assignment-based（基于赋值）的版本会首先调用 default constructors（缺省构造函数）初始化 theName，theAddress 和 thePhones，然而很快又在 default-constructed（缺省构造）的值之上赋予新值。那些 default constructions（缺省构造函数）所做的工作被浪费了。而 member initialization list（成员初始化列表）的方法避免了这个问题，因为<strong>initialization list（初始化列表</strong>中的 arguments（参数）就可以作为各种 data members（数据成员）的 constructor（构造函数）所使用的 arguments（参数）。在这种情况下，theName 从 name 中 copy-constructed（拷贝构造），theAddress 从 address 中 copy-constructed（拷贝构造），thePhones 从 phones 中 copy-constructed（拷贝构造）。对于大多数类型来说，只调用一次 copy constructor（拷贝构造函数）的效率比先调用一次 default constructor（缺省构造函数）再调用一次 copy assignment operator（拷贝赋值运算符）的效率要高（有时会高很多）。</li>
</ol>
<pre><code class="language-cpp">ABEntry::ABEntry(const std::string&amp; name, const std::string&amp; address,
                 const std::list&lt;PhoneNumber&gt;&amp; phones)

: theName(name),
  theAddress(address),                  // these are now all initializations
  thePhones(phones),
  numTimesConsulted(0)

{}    

</code></pre>
<p>在一个 class（类）内部，data members（数据成员）按照它们被声明的顺序被初始化。例如，在 ABEntry 中，theName 总是首先被初始化，theAddress 是第二个，thePhones 第三，numTimesConsulted 最后。即使它们在 member initialization list（成员初始化列表）中以一种不同的顺序排列（这不幸合法）.</p>
<ol start="7">
<li>
<p>赋值运算符函数需要注意的点，因为当前对象已经经过了初始化，所以已经有了存储空间，那么使用前就需要先<strong>释放</strong>之前申请的存储空间;另外，在赋值之前需要先判断这两个对象<strong>是否是同一个对象</strong>。</p>
</li>
<li>
<p>struct与class的区别，如果没有标明访问权限级别，在struct中默认的是public,而在class中默认的是private.如果 base classes（基类）将 copy assignment operator（拷贝赋值运算符）声明为 private，编译器拒绝为从它继承的 derived classes（派生类）生成 implicit copy assignment operators（隐式拷贝赋值运算符）。如果成员变量中有指针，引用或者const类型就需要自己定义默认构造函数、默认复制构造函数、默认赋值操作符。</p>
</li>
<li>
<p>将复制构造函数和赋值操作符函数声明为private，同时不给出定义就可以防止被复制。</p>
</li>
<li>
<p>polymorphic base classes（多态基类）应该声明 virtual destructor（虚拟析构函数）。如果一个 class（类）有任何 virtual functions（虚拟函数），它就应该有一个 virtual destructor（虚拟析构函数）。</p>
</li>
<li>
<p>高效的拷贝构造函数。</p>
</li>
</ol>
<pre><code class="language-cpp">Widget&amp; Widget::operator=(Widget rhs)   // rhs is a copy of the object
{                                       // passed in — note pass by val

  swap(rhs);                            // swap *this's data with
                                        // the copy's
  return *this;
}

</code></pre>
<ol start="12">
<li>当你写一个拷贝函数，需要保证（1）拷贝所有本地数据成员以及（2）调用所有基类中的适当的拷贝函数。</li>
</ol>
<pre><code class="language-cpp">PriorityCustomer::operator=(const PriorityCustomer&amp; rhs)
{
  logCall(&quot;PriorityCustomer copy assignment operator&quot;);

  Customer::operator=(rhs);           // assign base class parts
  priority = rhs.priority;

  return *this;
}

</code></pre>
<p>不要试图依据类内的一个拷贝函数实现同一类里的另一个拷贝函数。作为代替，将通用功能放入第三个供双方调用的函数</p>
<ol start="13">
<li>shared_ptr 和auto_ptr能够有效的管理堆上的资源，保证他们能够被释放，share_ptr更好用。而且支持拷贝资源管理。</li>
</ol>
<pre><code class="language-cpp">class Lock {
public:
 explicit Lock(Mutex *pm)       // init shared_ptr with the Mutex
 : mutexPtr(pm, unlock)         // to point to and the unlock func
 {                              // as the deleter

   lock(mutexPtr.get());        // see Item 15 for info on &quot;get&quot;
 }
private:
 std::tr1::shared_ptr&lt;Mutex&gt; mutexPtr;    // use shared_ptr
};

</code></pre>
<p>shared_ptr 和 auto_ptr 都提供一个 get 成员函数进行显示转换。</p>
<ol start="14">
<li>在一个独立的语句中将 new 出来的对象存入智能指针。</li>
<li>绝不要返回一个局部栈对象的指针或引用，绝不要返回一个被分配的堆对象的引用，如果存在需要一个以上这样的对象的可能性时，绝不要返回一个局部 static 对象的指针或引用。</li>
<li>在子类中使用与父类public成员函数同名的函数会造成覆盖，继续使用父类函数有两种方式：a.using b. 转调 <a href="https://wizardforcel.gitbooks.io/effective-cpp/content/35.html">Item 33: 避免覆盖（hiding）“通过继承得到的名字”</a></li>
<li></li>
</ol>
<h1 id="reference">reference</h1>
<p><a href="http://blog.csdn.net/wangshubo1989/article/details/50575008">c++11新特性</a></p>
<p><a href="https://wizardforcel.gitbooks.io/effective-cpp/content/">effective-cpp</a></p>
<p><a href="http://blog.yangyubo.com/2009/06/04/best-cpp-singleton-pattern/">c++单例模型</a></p>

          </div>
        </div>

        
          <div class="next-post">
            <a class="purple-link" href="https://dragonfive.gitee.io/post/mobile_net/">
              <h3 class="post-title">
                下一篇：mobile_net的模型优化
              </h3>
            </a>
          </div>
          
      </div>

      

      <div class="site-footer">
  <div class="slogan">邮箱(base64)：MTY5MDMwMjk2M0BxcS5jb20=
</div>
  <div class="social-container">
    
      
        <a href="https://github.com/DragonFive" target="_blank">
          <i class="fab fa-github"></i>
        </a>
      
    
      
    
      
    
      
    
      
    
  </div>
  Powered by <a href="https://github.com/getgridea/gridea" target="_blank">Gridea</a> | <a class="rss" href="https://dragonfive.gitee.io//atom.xml" target="_blank">RSS</a>
</div>


    </div>
    <script type="application/javascript">

hljs.initHighlightingOnLoad()

var app = new Vue({
  el: '#app',
  data: {
    menuVisible: false,
  },
})

</script>




  </body>
</html>
